/**
* The code dealing with selection of the Region of Influence, and
* specification of the anchor 
*
*/
#include <OGF/quick_start/common/deform.h>
#include <OGF/quick_start/common/select_util.h>
#include <OGF/basic/os/stopwatch.h>
#include <OGF/cells/map/map_cell_heap.h>

#include <set>
#include <stack>
#include <queue>
#include <limits>


#include <OGF/math/linear_algebra/vector.h>

namespace OGF 
{
	/**
	* Specify the rotation at the anchor. Rotation is about 
	* the axis, by the angle (in radians) given.
	*/
/*	void Deform::rotate_anchor_normal(Vector3d axis, double angle)
	{		
		axis.normalize();
		this->axis = axis;
		this->angle = angle;
	}*/

	/**
	* Prepare vertex lists for deformation.
	*  - Builds the complete list (verts)
	*  - record the row-number in the linear system (index)
	*/
	void Deform::populate_verts()
	{
		//If we haven't already got the lists, then build them.
		if( verts.empty() )
		{
			std::cout<<" Populating active vertex lists"<<std::endl;
			
			//Empty all the lists (should already be done)
			verts_border.clear();
			verts_guard.clear();
			verts_core.clear();
			verts.clear();
//			vert_start_pos.clear();

			//Add each vertex to the right list.
			FOR_EACH_VERTEX(Map, surface, v)
			{
				switch( (*classify)[v] )
				{
					case BORDER:
						verts_border.push_back(v);
						break;
					case GUARD:
						verts_guard.push_back(v);
						break;
					case CORE:
						verts_core.push_back(v);
						break;

					case ANCHOR:
					case NIL: 
						break;
					default:
						std::cerr<<"Error: unrecognized vertex classification "<<(*classify)[v]<<std::endl;
						break;
				}
			}

			//Build the big union list:
			verts.clear();		
			verts.push_back(anchor);
			verts.insert(verts.end(), verts_core.begin(), verts_core.end());
			verts.insert(verts.end(), verts_guard.begin(), verts_guard.end());
			verts.insert(verts.end(), verts_border.begin(), verts_border.end());

			FOR_EACH_VERTEX(Map, surface, v)
				(*index)[ v ] = -1; //bad value on bad vertices

			std::cout<<" Applying vertex index labels for back-reference"<<std::endl;
			for(unsigned int i=0;i<verts.size();i++)
				(*index)[ verts[i] ] = i;

			calc_alpha();
		}
		
		std::cout<<"  Region of influence contains:\n";
		std::cout<<"      "<<verts.size()<<" verticies\n";
		std::cout<<"      "<<"1 anchor vertex (laplace & locked)"<<std::endl;
		std::cout<<"      "<<verts_core.size()  <<" core verticies (laplace)\n";
		std::cout<<"      "<<verts_guard.size() <<" guard verticies (laplace & soft-locked)\n";
		std::cout<<"      "<<verts_border.size()<<" border verticies (locked)\n";
	}

	/**
	* Check if this vertex is in a border face. If it is, then
	* it must be a border vertex.
	*/
	bool Deform::touches_border_face(Map::Vertex* vert)
	{
		Map::Halfedge* first_edge = vert->halfedge();
		Map::Halfedge* edge = first_edge;
		do
		{
			if( edge->facet()!=nil && is_on_border(edge->facet()) )
				return true;
			edge = edge->next_around_vertex();
		}while(edge != first_edge);
		return false;
	}
	/**
	* Check if this face is a border face.
	*/
	bool Deform::is_on_border(Map::Facet* face)
	{
		ogf_assert(border != nil);
		return (*border)[face];
	}
	/**
	* Make the face a border. This also updates the 
	* positions of the surrounding (border and guard) 
	* verticies in the constraints. 
	*/
	void Deform::add_to_border(Map::Facet* face)
	{
		ogf_assert(border != nil);
		ogf_assert(face != nil);
		(*border)[face] = true;

		//adjacent verticies get guard positions set:
		Map::Vertex *vert;
		FOR_EACH_VERTEX_IN_FACE(face, vert)
		{
			Map::Vertex *v;
			FOR_EACH_ADJACENT_VERTEX(vert, v)
			{
				(*constraint_pos)[v] = v->point();
			}
		}
	}
	void Deform::remove_from_border(Map::Facet* face)
	{
		ogf_assert(border != nil);
		ogf_assert(face != nil);
		(*border)[face] = false;
	}
	/**
	* Check if this vertex is in the region of influence.
	*/
	bool Deform::is_in_region(Map::Vertex* vert)
	{
		ogf_assert(classify != nil);
		ogf_assert(vert != nil);
		int clas = (*classify)[vert];
		return clas != NIL;
		//return clas == BORDER || clas == GUARD || clas == CORE || clas == ANCHOR;
	}
	/**
	* Check if a facet is in the region of influence. 
	* This is used for the shader.
	*/
	bool Deform::is_in_region(Map::Facet* facet)
	{
		//a facet is in the region if any of its verts are.
		Map::Vertex *v;
		FOR_EACH_VERTEX_IN_FACE(facet, v)
		{
			if( is_in_region(v) )
				return true;
		}
		return false;
	}
	/**
	* Clear all region information. 
	* Lists of vertices, and attributes.
	*/
	void Deform::clear_region()
	{
		FOR_EACH_VERTEX(Map, this->surface, it)
		{
			(*classify)[ it ] = NIL;
		}
		verts_border.clear();
		verts_guard.clear();
		verts_core.clear();
		verts.clear();
//		vert_start_pos.clear();
	}

	/**
	* Return true if the anchor is good for performing deformations.
	* ie. Not null, and not a border vertex.
	*/
	bool Deform::is_anchor_good()
	{
		return anchor != nil && !touches_border_face(anchor);
	}

	/**
	* Discover the region of influence.
	* Start at the anchor, and flood-fill out to the boundary.
	* This classified all vertices as NIL, BORDER, CORE, GUARD
	*/
	void Deform::grow_region()
	{
		std::cout<<"Growing region of influence from anchor"<<std::endl;
		
		delete solver;	// The solver is out of date, since we
		solver = nil;   // are changing the region. So delete it.
		clear_region();	// Vertex lists are also expired.

		//Do nothing if the anchor isn't there, or is frozen.
		if( !is_anchor_good() ) 
			return;

		std::stack<Map::Vertex*> queue; //verticies to add to the ROI
		queue.push(anchor);

		while(!queue.empty()) //while there are more verticies to add...
		{
			Map::Vertex* vert = queue.top();
			queue.pop();
			ogf_assert(vert != nil);

			//Only explore a vertex that we haven't visited before
			if( (*classify)[vert] == NIL )
			{
				//Visit all the neighbors, and add them to the queue.
				
				(*classify)[vert] = CORE; //tentatively assign us as core.

				Map::Vertex* v;
				FOR_EACH_ADJACENT_VERTEX(vert, v)
				{
					//If we are adjacent to a border vertex, we are a guard
					if( touches_border_face(v) ) 
					{
						(*classify)[vert] = GUARD;
						(*classify)[v] = BORDER;
					}
					else
					{
						//Don't explore border faces.
						queue.push(v);
					}
				}
			}
		}
		//we hit the anchor somewhere and called it CORE/GUARD/BORDER - fix that.
		(*classify)[anchor] = ANCHOR;
	}

	/**
	* Set the anchor vertex to a new vertex.
	* Also grows the region around it.
	* Also calculate new deltas
	*/
	void Deform::set_anchor(Map::Vertex* vert)
	{
		//Don't change anything if we just selected the same anchor.
		if(anchor != vert)
		{
			this->anchor = vert;
			(*constraint_pos)[anchor] = anchor->point();
			grow_region();
		}
	}
	/**
	* Look up the anchor's normal.
	*/
	Vector3d Deform::get_true_anchor_normal()
	{
		MapTexVertexNormal normal(surface) ;
		return normal[anchor->halfedge()->tex_vertex()];
	}
	Map::Vertex* Deform::get_anchor()
	{
		return anchor;
	}

} //namespace